/*
 * =====================================================================================
 *
 *       Filename:  parameter_mcu.c
 *
 *    Description:
 *					1、内存参数获取
 *					2、提供参数获取接口给程序调用
 *
 *        Version:  1.0
 *        Created:  2019年1月23日 16时34分45秒
 *       Revision:  none
 *
 *         Author:  lixinde (lxd), lixinde@phytium.com.cn
 *        Company:  Phytium Technology Co.,Ltd
 *        License:  GPL-2.0+
 *
 * =====================================================================================
 */
#include "parameter_mcu.h"

static mcu_config_t const mcu_base_info = {
	.magic = PARAMETER_MCU_MAGIC,
	.version = CONFIG_MCU_PARAMETER_VERSION,
	.size = CONFIG_MCU_PARAMETER_SIZE,
	.ch_enable = CH_ENABLE_INFO,
	.ecc_enable = ECC_ENABLE_INFO,
	.dm_enable = DM_ENABLE_INFO,
	.force_spd_enable = CONFIG_SPD_FORCE_EN,
	.misc_enable = MCU_MIST_INFO,
	.train_debug = CONFIG_PHY_DEBUG_INFO,
	.train_recover = CONFIG_PHY_RECOVER_INFO,

#if CONFIG_SPD_FORCE_EN
	.ddr_spd_info[0] = {
		.dimm_type = CONFIG_DIMM_TYPE_0,
		.data_width = CONFIG_DATA_WIDTH_0,
		.mirror_type = CONFIG_MIRROR_TYPR_0,
		.ecc_type = CONFIG_ECC_TYPE_0,
		.dram_type = CONFIG_DRAM_TYPE_0,
		.rank_num = CONFIG_RANK_NUM_0,
		.row_num = CONFIG_ROW_NUM_0,
		.col_num = CONFIG_COL_NUM_0,
		.bg_num = CONFIG_BG_NUM_0,
		.bank_num = CONFIG_BANK_NUM_0,
		.module_manufacturer_id = 0x00,
		.tAAmin = CONFIG_T_AA_MIN_0,
		.tRCDmin = CONFIG_T_RCD_MIN_0,
		.tRPmin = CONFIG_T_RP_MIN_0,
		.tRASmin = CONFIG_T_RAS_MIN_0,
		.tRCmin = CONFIG_T_RC_MIN_0,
		.tFAWmin = CONFIG_T_FAW_MIN_0,
		.tRRD_Smin = CONFIG_T_RRD_S_MIN_0,
		.tRRD_Lmin = CONFIG_T_RRD_L_MIN_0,
		.tCCD_Lmin = CONFIG_T_CCD_L_MIN_0,
		.tWRmin = CONFIG_T_WR_MIN_0,
		.tWTR_Smin = CONFIG_T_WTR_S_MIN_0,
		.tWTR_Lmin = CONFIG_T_WTR_L_MIN_0,
		.tWTRmin = 0,
		.tRRDmin = 0,
		.F0RC03 = 0,
		.F0RC04 = 0,
		.F0RC05 = 0,
		.BC00 = 0,
		.BC01 = 0,
		.BC02 = 0,
		.BC03 = 0,
		.BC04 = 0,
		.BC05 = 0,
		.F5BC5x = 0,
		.F5BC6x = 0,
		.vrefDQ_PR0 = 0,
		.vrefDQ_MDRAM = 0,
		.RTT_MDRAM_1866 = 0,
		.RTT_MDRAM_2400 = 0,
		.RTT_MDRAM_3200 = 0,
		.Drive_DRAM = 0,
		.ODT_DRAM_1866 = 0,
		.ODT_DRAM_2400 = 0,
		.ODT_DRAM_3200 = 0,
		.PARK_DRAM_1866 = 0,
		.PARK_DRAM_2400 = 0,
		.PARK_DRAM_3200 = 0,
		.rcd_num = 0,
	},

	.ddr_spd_info[1] = {
		.dimm_type = CONFIG_DIMM_TYPE_1,
		.data_width = CONFIG_DATA_WIDTH_1,
		.mirror_type = CONFIG_MIRROR_TYPR_1,
		.ecc_type = CONFIG_ECC_TYPE_1,
		.dram_type = CONFIG_DRAM_TYPE_1,
		.rank_num = CONFIG_RANK_NUM_1,
		.row_num = CONFIG_ROW_NUM_1,
		.col_num = CONFIG_COL_NUM_1,
		.bg_num = CONFIG_BG_NUM_1,
		.bank_num = CONFIG_BANK_NUM_1,
		.module_manufacturer_id = 0x00,
		.tAAmin = CONFIG_T_AA_MIN_1,
		.tRCDmin = CONFIG_T_RCD_MIN_1,
		.tRPmin = CONFIG_T_RP_MIN_1,
		.tRASmin = CONFIG_T_RAS_MIN_1,
		.tRCmin = CONFIG_T_RC_MIN_1,
		.tFAWmin = CONFIG_T_FAW_MIN_1,
		.tRRD_Smin = CONFIG_T_RRD_S_MIN_1,
		.tRRD_Lmin = CONFIG_T_RRD_L_MIN_1,
		.tCCD_Lmin = CONFIG_T_CCD_L_MIN_1,
		.tWRmin = CONFIG_T_WR_MIN_1,
		.tWTR_Smin = CONFIG_T_WTR_S_MIN_1,
		.tWTR_Lmin = CONFIG_T_WTR_L_MIN_1,
		.tWTRmin = 0,
		.tRRDmin = 0,
		.F0RC03 = 0,
		.F0RC04 = 0,
		.F0RC05 = 0,
		.BC00 = 0,
		.BC01 = 0,
		.BC02 = 0,
		.BC03 = 0,
		.BC04 = 0,
		.BC05 = 0,
		.F5BC5x = 0,
		.F5BC6x = 0,
		.vrefDQ_PR0 = 0,
		.vrefDQ_MDRAM = 0,
		.RTT_MDRAM_1866 = 0,
		.RTT_MDRAM_2400 = 0,
		.RTT_MDRAM_3200 = 0,
		.Drive_DRAM = 0,
		.ODT_DRAM_1866 = 0,
		.ODT_DRAM_2400 = 0,
		.ODT_DRAM_3200 = 0,
		.PARK_DRAM_1866 = 0,
		.PARK_DRAM_2400 = 0,
		.PARK_DRAM_3200 = 0,
		.rcd_num = 0,
	},
#endif
};

int mcu_base_info_config(FILE *const fp, pm_info_t const *pm_d)
{
     if(NULL == fp){
        printf("fp is point to NULL\n");
		return -1;
    }
	fseek(fp, PM_MCU_OFFSET, 0);
	fwrite(&mcu_base_info, sizeof(mcu_base_info), 1, fp);
	return 0;
}

int get_mcu_info(FILE *const fp, parameter_base_t const *p_base)
{
	mcu_config_t mcu_parameter;
	mcu_config_t const * const pm = &mcu_parameter;

    if(NULL == fp){
		ERROR("fp is point NULL %s \n", __func__);
		return -1;
	}

	fseek(fp, p_base->addr + PM_MCU_OFFSET, 0);
	fread(&mcu_parameter, sizeof(mcu_config_t),1, fp);

  	if(PARAMETER_MCU_MAGIC != pm->magic){
		ERROR("can not get right magic 0x%x, %s \n", pm->magic, __func__);
		return -1;
	}

	printf("=======================================\n");

	printf("MCU info: \n");
	printf("  magic = 0x%x\n", pm->magic);
	printf("  version = 0x%x\n", pm->version);

	printf("  ECC: %s\n", IS_ENABLE(pm->ecc_enable));
	printf("  DM: %s\n", IS_ENABLE(pm->dm_enable));

	printf("  READ FREQ FROM SPD: %s\n", IS_ENABLE(pm->misc_enable & BIT(0)));
	printf("  USE_MARGIN: %s\n", IS_ENABLE(pm->misc_enable & BIT(1)));
	printf("  USE_S3_DEVINIT: %s\n", IS_ENABLE(pm->misc_enable & BIT(2)));
	printf("  2T_TIMING_MODE: %s\n", IS_ENABLE(pm->misc_enable & BIT(3)));
	printf("  DUAL_DIMM: %s\n", IS_ENABLE(pm->misc_enable & BIT(4)));
	printf("  PERF_MODE: %s\n", IS_ENABLE(pm->misc_enable & BIT(5)));
	printf("  2T_PREAMBLE_MODE: %s\n", IS_ENABLE(pm->misc_enable & BIT(6)));

	uint8_t ch;
	uint8_t temp;
	uint16_t sdram_density;		//Mb
	uint64_t dimm_capacity;		//G

	for(ch=0; ch<2; ch++){

		if(!((pm->ch_enable>>ch) & 0x1))
			continue;

		if(!(pm->force_spd_enable))
		{
			printf("  ch %d read from spd\n", ch);
			continue;
		}

		printf("  ch %d ddr info: \n", ch);
		switch(pm->ddr_spd_info[ch].dram_type){
			case 0xb:
				printf("\tDDR3"); break;
			case 0xc:
				printf("\tDDR4"); break;
			default:
				printf("\tdram_type=0x%x", pm->ddr_spd_info[ch].dram_type);
		}
		switch(pm->ddr_spd_info[ch].dimm_type){
			case 1:
				printf("\tRDIMM"); break;
			case 2:
				printf("\tUDIMM"); break;
			case 3:
			case 9:
				printf("\tSODIMM"); break;
			case 4:
				printf("\tLRDIMM"); break;
			default:
				printf("\tdimm_type=0x%x", pm->ddr_spd_info[ch].dimm_type);
		}
		temp = 4<<pm->ddr_spd_info[ch].data_width;

		sdram_density =
			(1UL<<(pm->ddr_spd_info[ch].row_num + pm->ddr_spd_info[ch].col_num)) * temp
			* pm->ddr_spd_info[ch].bg_num * pm->ddr_spd_info[ch].bank_num >> 20; //units: Mb

		printf("/%d Bank Groups", pm->ddr_spd_info[ch].bg_num);
		printf("/%d Banks", pm->ddr_spd_info[ch].bank_num);
		printf("/sdram_density=%dGb", sdram_density >> 10);

		printf("/Column %d", pm->ddr_spd_info[ch].col_num);
		printf("/Row %d", pm->ddr_spd_info[ch].row_num);

		printf("/%d Rank\n", pm->ddr_spd_info[ch].rank_num);
		switch(pm->ddr_spd_info[ch].ecc_type){
			case 0:
				printf("\tNO ECC"); break;
			case 1:
				printf("\tECC"); break;
			default:
				printf("\tecc_type=0x%x", pm->ddr_spd_info[ch].ecc_type);
		}
		if(pm->ddr_spd_info[ch].mirror_type == 0)
			printf("/Standard");
		else
			printf("/Mirror");

		printf("\n");
		dimm_capacity = sdram_density * pm->ddr_spd_info[ch].rank_num * 64 / temp >> 13;   //units: GB
		printf("\tDimm_Capacity = %ldGB\n", dimm_capacity );

		printf("  tAAmin = %dps\n", pm->ddr_spd_info[ch].tAAmin);
		printf("  tRCDmin = %dps\n", pm->ddr_spd_info[ch].tRCDmin);
	    printf("  tRPmin = %dps\n", pm->ddr_spd_info[ch].tRPmin);
	    printf("  tRASmin = %dps\n", pm->ddr_spd_info[ch].tRASmin);
	    printf("  tRCmin = %dps\n", pm->ddr_spd_info[ch].tRCmin);
	    printf("  tFAWmin = %dps\n", pm->ddr_spd_info[ch].tFAWmin);

		printf("  tRRD_Smin = %dps\n", pm->ddr_spd_info[ch].tRRD_Smin);
		printf("  tRRD_Lmin = %dps\n", pm->ddr_spd_info[ch].tRRD_Lmin);
		printf("  tCCD_Lmin = %dps\n", pm->ddr_spd_info[ch].tCCD_Lmin);
		printf("  tWRmin = %dps\n", pm->ddr_spd_info[ch].tWRmin);
		printf("  tWTR_Smin = %dps\n", pm->ddr_spd_info[ch].tWTR_Smin);
		printf("  tWTR_Lmin = %dps\n", pm->ddr_spd_info[ch].tWTR_Lmin);
	}
	printf("=======================================\n");

	return 0;
}

int check_mcu(FILE * const fp, uint64_t base)
{
	uint32_t magic;
	fseek(fp, base + PM_MCU_OFFSET, 0);
	fread(&magic,sizeof(magic),1,fp);
	if(PARAMETER_MCU_MAGIC != magic)
		return -1;
	else
		return 0;
}

pm_list_fun_t mcu_fun = {
	.cfg_fnc = mcu_base_info_config,
	.ck_fnc = check_mcu,
	.prf_fnc = get_mcu_info,
};

